 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.internal.mirror;

 import java.io.File ;
 import java.io.FileOutputStream ;
 import java.io.IOException ;
 import java.io.InputStream ;
 import java.io.OutputStreamWriter ;
 import java.io.PrintWriter ;
 import java.net.URL ;
 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.Set ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.update.core.ContentReference;
 import org.eclipse.update.core.ICategory;
 import org.eclipse.update.core.IFeature;
 import org.eclipse.update.core.IFeatureContentProvider;
 import org.eclipse.update.core.IFeatureReference;
 import org.eclipse.update.core.INonPluginEntry;
 import org.eclipse.update.core.IPluginEntry;
 import org.eclipse.update.core.ISite;
 import org.eclipse.update.core.ISiteFeatureReference;
 import org.eclipse.update.core.IURLEntry;
 import org.eclipse.update.core.IVerificationListener;
 import org.eclipse.update.core.Site;
 import org.eclipse.update.core.SiteFeatureReferenceModel;
 import org.eclipse.update.core.Utilities;
 import org.eclipse.update.core.VersionedIdentifier;
 import org.eclipse.update.core.model.CategoryModel;
 import org.eclipse.update.core.model.SiteModelFactory;
 import org.eclipse.update.core.model.URLEntryModel;
 import org.eclipse.update.internal.core.CoreExceptionWithRootCause;
 import org.eclipse.update.internal.core.FatalIOException;
 import org.eclipse.update.internal.core.FeaturePackagedContentProvider;
 import org.eclipse.update.internal.core.ISiteContentConsumer;
 import org.eclipse.update.internal.core.UpdateCore;
 import org.eclipse.update.internal.core.UpdateManagerUtils;
 import org.eclipse.update.standalone.StandaloneUpdateApplication;

 /**
  * Local mirror site. Read/Write
  */
 public class MirrorSite extends Site {
     private final static String INDENT = " "; //$NON-NLS-1$
 private SiteModelFactory factory;
     /**
      * plugin entries
      */
     private Collection downloadedPluginEntries = new ArrayList ();
     private Collection downloadedFeatureReferenceModels = new ArrayList ();
     private boolean ignoreNonPresentPlugins;
     public MirrorSite(SiteModelFactory factory) {
         this.factory = factory;
     }

     /**
      * Mirrors the specified features and listed optional features on this site.
      * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
      * @param mirrorSiteUrl external URL of the mirror site or null;
      * if parameter is provided policy fragment will be generated
      * @exception CoreException
      */
     public void mirrorAndExpose(
         ISite remoteSite,
         ISiteFeatureReference[] sourceFeatureRefs,
         IFeatureReference[] optionalfeatures,
         String mirrorSiteUrl)
         throws CoreException {

         mirrorAndExposeFeatures(
             remoteSite,
             sourceFeatureRefs,
             optionalfeatures);

         System.out.println(
             "Installing features finished. Updating categories ..."); //$NON-NLS-1$
 updateCategories(remoteSite);
         System.out.println(
             "Updating categories finished. Updating site description ..."); //$NON-NLS-1$
 updateDescription(remoteSite);
         System.out.println(
             "Updating site description finished. Saving site.xml ..."); //$NON-NLS-1$
 save();
         if (mirrorSiteUrl != null) {
             generateUpdatePolicy(mirrorSiteUrl);
         }
     }
     private void mirrorAndExposeFeatures(
         ISite remoteSite,
         ISiteFeatureReference[] sourceFeatureRefs,
         IFeatureReference[] optionalfeatures)
         throws CoreException {

         // Features that failed will be retried once again
 Collection failedFeatures = new ArrayList ();
         for (int i = 0; i < sourceFeatureRefs.length; i++) {
             try {
                 IFeature sourceFeature =
                     sourceFeatureRefs[i].getFeature(new NullProgressMonitor());
                 SiteFeatureReferenceModel featureRef =
                     mirrorFeature(
                         remoteSite,
                         sourceFeature,
                         optionalfeatures,
                         1);
                 // Set categories of the new feature
 ICategory remoteCategories[] =
                     sourceFeatureRefs[i].getCategories();
                 for (int j = 0; j < remoteCategories.length; j++) {
                     featureRef.addCategoryName(remoteCategories[j].getName());
                 }

                 addFeatureReferenceModel(remoteSite, featureRef);
             } catch (CoreException ce) {
                 failedFeatures.add(sourceFeatureRefs[i]);
             }
         }

         // do we need to retry?
 if (failedFeatures.size() > 0) {
             sourceFeatureRefs =
                 (ISiteFeatureReference[]) failedFeatures.toArray(
                     new ISiteFeatureReference[failedFeatures.size()]);
         } else {
             return;
         }

         for (int i = 0; i < sourceFeatureRefs.length; i++) {
             IFeature sourceFeature =
                 sourceFeatureRefs[i].getFeature(new NullProgressMonitor());
             SiteFeatureReferenceModel featureRef =
                 mirrorFeature(remoteSite, sourceFeature, optionalfeatures, 1);
             // Set categories of the new feature
 ICategory remoteCategories[] = sourceFeatureRefs[i].getCategories();
             for (int j = 0; j < remoteCategories.length; j++) {
                 featureRef.addCategoryName(remoteCategories[j].getName());
             }

             addFeatureReferenceModel(remoteSite, featureRef);
         }
     }

     /**
      * Install the specified feature and listed optional features on this site.
      * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
      * @exception CoreException
      */
     private SiteFeatureReferenceModel mirrorFeature(
         ISite remoteSite,
         IFeature sourceFeature,
         IFeatureReference[] optionalfeatures,
         int indent)
         throws CoreException {
         String tab = ""; //$NON-NLS-1$
 for (int i = 0; i < indent; i++)
             tab += " "; //$NON-NLS-1$
 System.out.println(
             tab
                 + "Mirroring feature " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 SiteFeatureReferenceModel existingFeatures[] =
             getDownloadedFeatureReferenceModels();
         for (int e = 0; e < existingFeatures.length; e++) {
             if (existingFeatures[e]
                 .getVersionedIdentifier()
                 .equals(sourceFeature.getVersionedIdentifier())) {
                 System.out.println(
                     tab
                         + "Feature " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                         + " already exists. Skipping downloading."); //$NON-NLS-1$
 return existingFeatures[e];
             }
         }

         final IFeatureContentProvider provider =
             sourceFeature.getFeatureContentProvider();
         
         // TODO: passing command options could be made more general in future, so this
 // cast is not needed.
 if (provider instanceof FeaturePackagedContentProvider) {
             ((FeaturePackagedContentProvider) provider).setContinueOnError(ignoreNonPresentPlugins);
         }
         
         System.out.println(
             tab
                 + "Getting plugin entries for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 final IPluginEntry[] sourceFeaturePluginEntries =
             sourceFeature.getRawPluginEntries();

         // determine list of plugins to install
 // find the intersection between the plugin entries already contained
 // on the target site, and plugin entries packaged in source feature

         IPluginEntry[] pluginsToInstall =
             UpdateManagerUtils.diff(
                 sourceFeaturePluginEntries,
                 getDownloadedPluginEntries());

         System.out.println(
             tab
                 + "Getting non plugin entries for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 final INonPluginEntry[] nonPluginsToInstall =
             sourceFeature.getRawNonPluginEntries();

         System.out.println(
             tab
                 + "Getting included features for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 IFeatureReference[] children =
             sourceFeature.getRawIncludedFeatureReferences();
         if (optionalfeatures != null) {
             children =
                 UpdateManagerUtils.optionalChildrenToInstall(
                     children,
                     optionalfeatures);
         }

         System.out.println(
             tab
                 + "Downloading feature archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // download feature archives
 provider.getFeatureEntryArchiveReferences(null);

         System.out.println(
             tab
                 + "Downloading plug-in archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // download plugin archives
 for (int i = 0; i < pluginsToInstall.length; i++) {
             try {
                 provider.getPluginEntryArchiveReferences(pluginsToInstall[i], null);
             } catch (CoreException ce) {
                 if ( ignoreNonPresentPlugins &&
                         (ce instanceof CoreExceptionWithRootCause) &&
                         (((CoreExceptionWithRootCause)ce).getRootException() != null) &&
                         (((CoreExceptionWithRootCause)ce).getRootException() instanceof FatalIOException) ) {
                     System.out.println("Could not mirror plug-in " + pluginsToInstall[i].getVersionedIdentifier().toString() + ". It does not exist on the given site"); //$NON-NLS-1$//$NON-NLS-2$
 } else {
                     throw ce;
                 }
             }
         }

         System.out.println(
             tab
                 + "Downloading non plug-in archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // download non-plugin archives
 for (int i = 0; i < nonPluginsToInstall.length; i++) {
             provider.getNonPluginEntryArchiveReferences(
                 nonPluginsToInstall[i],
                 null);
         }

         System.out.println(
             tab
                 + "Installing child features for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // install child features first
 for (int i = 0; i < children.length; i++) {
             IFeature childFeature = children[i].getFeature(null);
             mirrorFeature(
                 remoteSite,
                 childFeature,
                 optionalfeatures,
                 indent + 1);
         }

         System.out.println(
             tab
                 + "Storing plug-in archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // store plugins' archives
 for (int i = 0; i < pluginsToInstall.length; i++) {
             try {
                 ContentReference[] references = provider.getPluginEntryArchiveReferences( pluginsToInstall[i], null);
                 storePluginArchive(references[0]);
                 addDownloadedPluginEntry(pluginsToInstall[i]);
             } catch (CoreException ce) {
                 if ( ignoreNonPresentPlugins &&
                         (ce instanceof CoreExceptionWithRootCause) &&
                         (((CoreExceptionWithRootCause)ce).getRootException() != null) &&
                         (((CoreExceptionWithRootCause)ce).getRootException() instanceof FatalIOException) ) {
                     System.out.println("Could not write plug-in " + pluginsToInstall[i].getVersionedIdentifier().toString() + ". It does not exist on the given site"); //$NON-NLS-1$ //$NON-NLS-2$
 } else {
                     //System.out.println("ignoreNonPresentPlugins:"+ignoreNonPresentPlugins); //$NON-NLS-1$
 throw ce;
                 }
             }
         }

         System.out.println(
             tab
                 + "Storing non plug-in archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // store non plugins' archives
 for (int i = 0; i < nonPluginsToInstall.length; i++) {
             ContentReference[] references =
                 provider.getNonPluginEntryArchiveReferences(
                     nonPluginsToInstall[i],
                     null);
             for (int r = 0; r < references.length; r++) {
                 storeNonPluginArchive(
                     sourceFeature.getVersionedIdentifier(),
                     references[r]);
             }
         }

         System.out.println(
             tab
                 + "Storing feature archives for " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " ..."); //$NON-NLS-1$
 // store feature archive
 ContentReference[] references =
             provider.getFeatureEntryArchiveReferences(null);
         storeFeatureArchive(references[0]);

         System.out.println(
             tab
                 + "Adding feature " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " to model ..."); //$NON-NLS-1$

         // add feature model to site model
 SiteFeatureReferenceModel featureRef =
             factory.createFeatureReferenceModel();
         featureRef.setSiteModel(this);
         //featureRef.setURLString(featureURL.toExternalForm());
 featureRef.setType(ISite.DEFAULT_PACKAGED_FEATURE_TYPE);
         featureRef.setFeatureIdentifier(
             sourceFeature.getVersionedIdentifier().getIdentifier());
         featureRef.setFeatureVersion(
             sourceFeature.getVersionedIdentifier().getVersion().toString());
         addDownloadedFeatureReferenceModel(featureRef);

         System.out.println(
             tab
                 + "Mirroring feature " //$NON-NLS-1$
 + sourceFeature.getVersionedIdentifier()
                 + " finished."); //$NON-NLS-1$
 return featureRef;

     }
     /**
      * Adds a feature reference model to this site,
      * and exposes in site.xml if remote site exposes given feature.
      */
     public void addFeatureReferenceModel(
         ISite remoteSite,
         SiteFeatureReferenceModel featureReference) {
         // check if remote site exposes this feature
 ISiteFeatureReference remoteFeatures[] =
             remoteSite.getRawFeatureReferences();
         for (int i = 0; i < remoteFeatures.length; i++) {
             ISiteFeatureReference remoteFeatureRef = remoteFeatures[i];
             try {
                 if (remoteFeatureRef
                     .getVersionedIdentifier()
                     .equals(featureReference.getVersionedIdentifier())) {
                     addFeatureReferenceModel(featureReference);
                 }
             } catch (CoreException ce) {
                 StandaloneUpdateApplication.exceptionLogged();
                 UpdateCore.log(ce);
             }
         }
         save();
         System.out.println(
             "Feature " //$NON-NLS-1$
 + featureReference.getVersionedIdentifier()
                 + " added to site.xml."); //$NON-NLS-1$
 }
     /**
      * Adds feature model to site model, removing old feature
      */
     public void addFeatureReferenceModel(SiteFeatureReferenceModel featureReference) {
         SiteFeatureReferenceModel[] existingModels =
             getFeatureReferenceModels();
         for (int j = 0; j < existingModels.length; j++) {
             if (existingModels[j]
                 .getVersionedIdentifier()
                 .equals(featureReference.getVersionedIdentifier())) {
                 super.removeFeatureReferenceModel(existingModels[j]);
             }
         }
         super.addFeatureReferenceModel(featureReference);
     }

     /**
      * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
      */
     private void storeFeatureArchive(ContentReference contentReference)
         throws CoreException {
         InputStream inStream = null;
         String featurePath = null;

         try {
             URL newURL =
                 new URL (
                     this.getURL(),
                     Site.DEFAULT_INSTALLED_FEATURE_PATH
                         + contentReference.getIdentifier()
                         + ".jar"); //$NON-NLS-1$
 featurePath = newURL.getFile();
             inStream = contentReference.getInputStream();
             UpdateManagerUtils.copyToLocal(inStream, featurePath, null);
         } catch (IOException e) {
             throw Utilities.newCoreException(
                 "Error occurred while creating "+ featurePath+" file.", //$NON-NLS-1$ //$NON-NLS-2$
 e);
         } finally {
             if (inStream != null) {
                 try {
                     inStream.close();
                 } catch (IOException e) {
                 }
             }
         }

     }
     /**
     * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
     */
     private void storePluginArchive(ContentReference contentReference)
         throws CoreException {

         InputStream inStream = null;
         String pluginPath = null;
         try {
             URL newURL = new URL (getURL(), contentReference.getIdentifier());
             pluginPath = newURL.getFile();
             inStream = contentReference.getInputStream();
             // added null check here, since contentReference can, in theory, return null for input stream.
 if (inStream != null) {
                 UpdateManagerUtils.copyToLocal(inStream, pluginPath, null);
             }
         } catch (IOException e) {
             throw Utilities.newCoreException(
             "Error occurred while creating "+ pluginPath+" file.", //$NON-NLS-1$ //$NON-NLS-2$
 e);
         } finally {
             if (inStream != null) {
                 try {
                     inStream.close();
                 } catch (IOException e) {
                 }
             }
         }
     }

     private void storeNonPluginArchive(
         VersionedIdentifier featureVersionedIdentifier,
         ContentReference contentReference)
         throws CoreException {

         InputStream inStream = null;
         File nonPluginArchivePath = null;
         try {
             URL newDirURL =
                 new URL (
                     getURL(),
                     Site.DEFAULT_INSTALLED_FEATURE_PATH
                         + "/" //$NON-NLS-1$
 + featureVersionedIdentifier);
             File dir = new File (newDirURL.getFile());
             dir.mkdirs();
             inStream = contentReference.getInputStream();
             nonPluginArchivePath =
                 new File (dir, contentReference.getIdentifier());
             UpdateManagerUtils.copyToLocal(
                 inStream,
                 nonPluginArchivePath.getAbsolutePath(),
                 null);
         } catch (IOException e) {
             throw Utilities.newCoreException(
             "Error occurred while creating "+ nonPluginArchivePath.getAbsolutePath()+" file." //$NON-NLS-1$ //$NON-NLS-2$
 ,e);
         } finally {
             if (inStream != null) {
                 try {
                     inStream.close();
                 } catch (IOException e) {
                 }
             }
         }
     }

     private void save() {
         FileOutputStream fos = null;
         try {
             URL siteURL = new URL (this.getURL(), "site.xml"); //$NON-NLS-1$
 fos = new FileOutputStream (new File (siteURL.getFile()));
             OutputStreamWriter outWriter = new OutputStreamWriter (fos, "UTF-8"); //$NON-NLS-1$
 PrintWriter writer = new PrintWriter (outWriter);
             save(writer);
             writer.flush();
         } catch (IOException ioe) {
             StandaloneUpdateApplication.exceptionLogged();
             UpdateCore.log(
                 Utilities.newCoreException(
                     "Site XML could not be saved.", //$NON-NLS-1$
 ioe));
         } finally {
             if (fos != null) {
                 try {
                     fos.close();
                 } catch (IOException ioe2) {
                 }
             }
         }
     }
     private void save(PrintWriter writer) {
         writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
 //writer.println("<!DOCTYPE site SYSTEM \"dtd/site.dtd\">");
 writeSite("", writer); //$NON-NLS-1$
 }

     private void writeSite(String indent, PrintWriter writer) {
         writer.print(indent + "<site"); //$NON-NLS-1$
 String indent2 = indent + INDENT;
         // default type
 //writeIfDefined(indenta, writer, "type", getType());
 // stored relative to site.xml
 //writeIfDefined(indenta, writer, "url", getURL());
 writer.println(">"); //$NON-NLS-1$
 URLEntryModel description = getDescriptionModel();
         if (description != null) {
             writer.println();
             writeDescription(indent2, writer, description);
             writer.println();
         }
         writeFeatures(indent2, writer);
         writeCategories(indent2, writer);
         writer.println(indent + "</site>"); //$NON-NLS-1$
 }
     private void writeFeatures(String indent, PrintWriter writer) {
         SiteFeatureReferenceModel[] featureReferenceModels =
             getFeatureReferenceModels();
         for (int i = 0; i < featureReferenceModels.length; i++) {
             writer.print(indent);
             writer.print("<feature"); //$NON-NLS-1$
 writer.print(
                 " url=\"features/" //$NON-NLS-1$
 + featureReferenceModels[i].getFeatureIdentifier()
                     + "_" //$NON-NLS-1$
 + featureReferenceModels[i].getFeatureVersion()
                     + ".jar\""); //$NON-NLS-1$
 writer.print(
                 " id=\"" //$NON-NLS-1$
 + featureReferenceModels[i].getFeatureIdentifier()
                     + "\""); //$NON-NLS-1$
 writer.print(
                 " version=\"" //$NON-NLS-1$
 + featureReferenceModels[i].getFeatureVersion()
                     + "\""); //$NON-NLS-1$
 writer.println(">"); //$NON-NLS-1$

             String [] categoryNames =
                 featureReferenceModels[i].getCategoryNames();
             for (int cn = 0; cn < categoryNames.length; cn++) {
                 writer.print(indent + INDENT);
                 writer.println(
                     "<category name=\"" + categoryNames[cn] + "\" />"); //$NON-NLS-1$ //$NON-NLS-2$

             }

             writer.print(indent);
             writer.println("</feature>"); //$NON-NLS-1$
 writer.println();
         }
     }
     private void writeCategories(String indent, PrintWriter writer) {
         CategoryModel[] categoryModels = getCategoryModels();
         if (categoryModels.length <= 0) {
             return;
         }
         for (int i = 0; i < categoryModels.length; i++) {
             writer.print(indent);
             writer.print("<category-def"); //$NON-NLS-1$
 writer.print(
                 " name=\"" //$NON-NLS-1$
 + categoryModels[i].getName()
                     + "\" label=\"" //$NON-NLS-1$
 + categoryModels[i].getLabel()
                     + "\""); //$NON-NLS-1$
 writer.println(">"); //$NON-NLS-1$
 if (categoryModels[i].getDescriptionModel() != null) {
                 writeDescription(
                         indent + INDENT,
                         writer,
                         categoryModels[i].getDescriptionModel());
             }
             writer.print(indent);
             writer.println("</category-def>"); //$NON-NLS-1$
 writer.println();
         }
     }
     private void writeDescription(
         String indent,
         PrintWriter writer,
         URLEntryModel urlEntryModel) {
         String url = urlEntryModel.getURLString();
         String text = urlEntryModel.getAnnotationNonLocalized();
         if (url == null && text == null && text.length() <= 0) {
             return;
         }
         writer.print(indent);
         writer.print("<description"); //$NON-NLS-1$
 if (url != null)
             writer.print(" url=\"" + url + "\""); //$NON-NLS-1$ //$NON-NLS-2$
 if (text == null || text.length() <= 0) {
             writer.println(" />"); //$NON-NLS-1$
 } else {
             writer.println(">"); //$NON-NLS-1$
 if (text != null) {
                 writer.println(
                     indent + INDENT + UpdateManagerUtils.Writer.xmlSafe(text));
             }
             writer.println(indent + "</description>"); //$NON-NLS-1$
 }
     }
     /**
      * Adds a plugin entry
      * Either from parsing the file system or
      * installing a feature
      *
      * We cannot figure out the list of plugins by reading the Site.xml as
      * the archives tag are optionals
      */
     public void addDownloadedPluginEntry(IPluginEntry pluginEntry) {
         downloadedPluginEntries.add(pluginEntry);
     }

     private IPluginEntry[] getDownloadedPluginEntries() {
         return (IPluginEntry[]) downloadedPluginEntries.toArray(
             new IPluginEntry[downloadedPluginEntries.size()]);
     }
     /**
      * Adds a plugin entry
      * Either from parsing the file system or
      * installing a feature
      *
      * We cannot figure out the list of plugins by reading the Site.xml as
      * the archives tag are optionals
      */
     public void addDownloadedFeatureReferenceModel(SiteFeatureReferenceModel featureModel) {
         downloadedFeatureReferenceModels.add(featureModel);
     }

     private SiteFeatureReferenceModel[] getDownloadedFeatureReferenceModels() {
         return (
             SiteFeatureReferenceModel[]) downloadedFeatureReferenceModels
                 .toArray(
             new SiteFeatureReferenceModel[downloadedFeatureReferenceModels
                 .size()]);
     }
     /**
      * Checks if mirror site contains a feature with given ID and version
      * @param featureRefModel
      * @return true if such feature exists
      */
     /*private boolean contains(SiteFeatureReferenceModel featureRefModel) {
         ISiteFeatureReference featureRefs[] = getRawFeatureReferences();
         for (int i = 0; i < featureRefs.length; i++) {
             try {
                 if (featureRefs[i]
                     .getVersionedIdentifier()
                     .equals(featureRefModel.getVersionedIdentifier())) {
                     return true;
                 }
             } catch (CoreException ce) {
                 ce.printStackTrace();
             }
         }
         return false;
     }*/

     /**
      * Updates description of this site
      * from description of the remote site.
      */
     private void updateDescription(ISite remoteSite) {
         IURLEntry urlEntry = remoteSite.getDescription();
         if (urlEntry != null) {
             URLEntryModel newUrlEntryModel = new URLEntryModel();
             URL url = urlEntry.getURL();
             newUrlEntryModel.setAnnotation(urlEntry.getAnnotation());
             // https://bugs.eclipse.org/bugs/show_bug.cgi?id=136249
 // URL is not required, so might be null
 // The null case is (already) handled correctly in
 // writeDescription
 if (url != null) {
                 newUrlEntryModel.setURLString(url.toExternalForm());
             }
             this.setDescriptionModel(newUrlEntryModel);
         }
     }
     /**
      * Updates all categories used by features on this site
      * from categories defined on remote site.
      * Categories not defined on remote site are unchanged.
      */
     private void updateCategories(ISite remoteSite) {
         // collect name of categories used on this site
 Set usedCategoryNames = new HashSet ();
         SiteFeatureReferenceModel featureRefModels[] =
             getFeatureReferenceModels();
         for (int f = 0; f < featureRefModels.length; f++) {
             String [] featureCategoryNames =
                 featureRefModels[f].getCategoryNames();

             for (int c = 0; c < featureCategoryNames.length; c++) {
                 usedCategoryNames.add(featureCategoryNames[c]);
             }
         }

         Collection newCategoryModels = new ArrayList ();
         for (Iterator it = usedCategoryNames.iterator(); it.hasNext();) {
             String name = (String ) it.next();
             ICategory remoteCategory = remoteSite.getCategory(name);
             if (remoteCategory == null) {
                 // remote site does not define this category
 CategoryModel oldCategory = null;
                 try {
                     oldCategory = (CategoryModel) getCategory(name);
                 } catch (NullPointerException npe) {
                     // cannot reproduce npe anymore
 }
                 if (oldCategory != null) {
                     newCategoryModels.add(oldCategory);
                 }
             } else {
                 newCategoryModels.add(remoteCategory);
             }

         }
         setCategoryModels(
             (CategoryModel[]) newCategoryModels.toArray(
                 new CategoryModel[newCategoryModels.size()]));

     }
     private void generateUpdatePolicy(String url) {
         FileOutputStream fos = null;
         try {
             URL siteURL = new URL (this.getURL(), "policy.xml"); //$NON-NLS-1$
 fos = new FileOutputStream (new File (siteURL.getFile()));
             OutputStreamWriter outWriter = new OutputStreamWriter (fos, "UTF-8"); //$NON-NLS-1$
 PrintWriter writer = new PrintWriter (outWriter);

             writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
 writer.println("<update-policy>"); //$NON-NLS-1$

             writer.println(
                 "<!-- You can paste the following fragment, containing url-map elements, into another policy file. -->"); //$NON-NLS-1$
 writeUrlMaps(writer, url);
             writer.println("<!-- End of fragment with url-map elements. -->"); //$NON-NLS-1$

             writer.println("</update-policy>"); //$NON-NLS-1$

             writer.flush();
         } catch (IOException ioe) {
             StandaloneUpdateApplication.exceptionLogged();
             UpdateCore.log(
                 Utilities.newCoreException(
                     "policy.xml could not be saved", //$NON-NLS-1$
 ioe));
         } finally {
             if (fos != null) {
                 try {
                     fos.close();
                 } catch (IOException ioe2) {
                 }
             }
         }
     }
     private void writeUrlMaps(PrintWriter writer, String url) {
         SiteFeatureReferenceModel[] featureReferenceModels =
             getFeatureReferenceModels();
         for (int i = 0; i < featureReferenceModels.length; i++) {
             writer.print("\t"); //$NON-NLS-1$
 writer.print("<url-map"); //$NON-NLS-1$
 writer.print(
                 " pattern=\"" //$NON-NLS-1$
 + featureReferenceModels[i].getFeatureIdentifier()
                     + "\""); //$NON-NLS-1$
 writer.print(" url=\"" + url + "\""); //$NON-NLS-1$ //$NON-NLS-2$
 writer.println(" />"); //$NON-NLS-1$
 }
     }

     public void setIgnoreNonPresentPlugins(boolean ignoreNonPresentPlugins) {
         this.ignoreNonPresentPlugins = ignoreNonPresentPlugins;
         
     }
 }

